
#include "stm32f4xx.h"
#include "board.h"

#define EXT_SRAM_ADDR    ((uint32_t)(0x68000000))
#define EXT_SRAM_SIZE    ((uint32_t)(0x00100000))

static volatile uint32_t _tick = 0;
uint32_t driv_tick_get(void)
{
    uint32_t primask = __get_PRIMASK();
    __disable_irq();
    uint32_t tick = _tick;
    __set_PRIMASK(primask);

    return tick;
}

void driv_tick_inc(void)
{
    _tick ++;
}

uint32_t driv_tick_elaps(uint32_t prev_tick)
{
    uint32_t act_time = driv_tick_get();

    if(act_time >= prev_tick) {
        prev_tick = act_time - prev_tick;
    }
    else {
        prev_tick = UINT32_MAX - prev_tick + 1;
        prev_tick += act_time;
    }
    return prev_tick;
}

int hw_delay_ms(uint32_t ms)
{
    uint32_t t = driv_tick_get();
    while(driv_tick_elaps(t) < ms) {
        /* Do something to no call `ev_tick_get` too often,
           as it interfere with interrupts
        */
        volatile uint32_t i;
        volatile uint32_t x = ms;
        for(i = 0; i < 200; i++) {
            x = x * 3;
        }
    }
    return 0;
}

const char *driv_get_board(void)
{
    return "QIMIN407";
}

void SystemClock_Config(void)
{
    ErrorStatus HSEStartUpStatus;

    /* Configure voltage regulator to Scale1 mode */
    PWR_MainRegulatorModeConfig(PWR_Regulator_Voltage_Scale1);

    /* Enable HSE (High Speed External oscillator) */
    RCC_HSEConfig(RCC_HSE_ON);

    /* Wait until HSE is ready */
    HSEStartUpStatus = RCC_WaitForHSEStartUp();
    if (HSEStartUpStatus == SUCCESS)
    {
        /* Configure PLL parameters */
        // PLL configuration: HSE (8MHz) as source
        // PLLM = 4, PLLN = 168, PLLP = 2, PLLQ = 4
        // VCO input frequency = HSE / PLLM = 8MHz / 4 = 2MHz
        // VCO output frequency = VCO input × PLLN = 2MHz × 168 = 336MHz
        // PLL clock output = VCO frequency / PLLP = 336MHz / 2 = 168MHz
        RCC_PLLCmd(DISABLE);
        /* Wait until PLL is ready */
        while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) != RESET)
        {
        }

        RCC_PLLConfig(RCC_PLLSource_HSE,       // PLL source = HSE
                      4,                       // PLLM = 4
                      168,                     // PLLN = 168
                      2,                       // PLLP = 2
                      4);                      // PLLQ = 4

        /* Enable PLL */
        RCC_PLLCmd(ENABLE);

        /* Wait until PLL is ready */
        while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
        {
        }

        /* Select PLL as system clock source */
        RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

        /* Wait until PLL is used as system clock source */
        while (RCC_GetSYSCLKSource() != 0x08)
        {
        }
        /* Configure Flash prefetch, instruction cache, data cache, and wait states */
        FLASH_SetLatency(FLASH_Latency_5);

        /* Configure AHB, APB1, and APB2 prescalers */
        RCC_HCLKConfig(RCC_SYSCLK_Div1);      // AHB clock = SYSCLK / 1
        RCC_PCLK1Config(RCC_HCLK_Div4);       // APB1 clock = HCLK / 4
        RCC_PCLK2Config(RCC_HCLK_Div2);       // APB2 clock = HCLK / 2
    }
    else
    {
        /* HSE startup failed - add error handling here */
        while (1)
        {
        }
    }
}

static void peripheral_clock_op(FunctionalState state)
{
    // AHB1
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB |
                            RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD |
                            RCC_AHB1Periph_GPIOE | RCC_AHB1Periph_GPIOF |
                            RCC_AHB1Periph_GPIOG | RCC_AHB1Periph_GPIOH |
                            RCC_AHB1Periph_DMA1 | RCC_AHB1Periph_DMA2, state);

    // AHB3
    RCC_AHB3PeriphClockCmd(RCC_AHB3Periph_FSMC, state);

    // APB1
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7 | RCC_APB1Periph_USART3 |
                            RCC_APB1Periph_PWR | RCC_APB1Periph_TIM4, state);

    // APB2
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_USART6 |
                            RCC_APB2Periph_ADC1 | RCC_APB2ENR_SYSCFGEN, state);
}

static void gpio_init(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;

    // lcd backlight
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOF, &GPIO_InitStructure);

    // run led
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOE, &GPIO_InitStructure);

    //fsmc
    GPIO_InitStructure.GPIO_Pin = 0xFF33; // 0-1-4-5-8-9-10-11-12-13-14-15
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOD, &GPIO_InitStructure);
    GPIO_PinAFConfig(GPIOD,GPIO_PinSource0,GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOD,GPIO_PinSource1,GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOD,GPIO_PinSource4,GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOD,GPIO_PinSource5,GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOD,GPIO_PinSource8,GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOD,GPIO_PinSource9,GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOD,GPIO_PinSource10,GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOD,GPIO_PinSource11,GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOD,GPIO_PinSource12,GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOD,GPIO_PinSource13,GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOD,GPIO_PinSource14,GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOD,GPIO_PinSource15,GPIO_AF_FSMC);

    GPIO_InitStructure.GPIO_Pin = 0xFF83; // 0-1-7-8-9-10-11-12-13-14-15
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOE, &GPIO_InitStructure);
    GPIO_PinAFConfig(GPIOE,GPIO_PinSource0,GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOE,GPIO_PinSource1,GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOE,GPIO_PinSource7,GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOE,GPIO_PinSource8,GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOE,GPIO_PinSource9,GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOE,GPIO_PinSource10,GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOE,GPIO_PinSource11,GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOE,GPIO_PinSource12,GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOE,GPIO_PinSource13,GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOE,GPIO_PinSource14,GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOE,GPIO_PinSource15,GPIO_AF_FSMC);

    GPIO_InitStructure.GPIO_Pin = 0xF03F; // 0-1-2-3-4-5-12-13-14-15
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOF, &GPIO_InitStructure);
    GPIO_PinAFConfig(GPIOF,GPIO_PinSource0,GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOF,GPIO_PinSource1,GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOF,GPIO_PinSource2,GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOF,GPIO_PinSource3,GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOF,GPIO_PinSource4,GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOF,GPIO_PinSource5,GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOF,GPIO_PinSource12,GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOF,GPIO_PinSource13,GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOF,GPIO_PinSource14,GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOF,GPIO_PinSource15,GPIO_AF_FSMC);

    GPIO_InitStructure.GPIO_Pin = 0x143F; // 0-1-2-3-4-5-10-12
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOG, &GPIO_InitStructure);
    GPIO_PinAFConfig(GPIOG,GPIO_PinSource0,GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOG,GPIO_PinSource1,GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOG,GPIO_PinSource2,GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOG,GPIO_PinSource3,GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOG,GPIO_PinSource4,GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOG,GPIO_PinSource5,GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOG,GPIO_PinSource10,GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOG,GPIO_PinSource12,GPIO_AF_FSMC);

    // usart1
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);
    GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);

    // usart3
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    GPIO_PinAFConfig(GPIOB,GPIO_PinSource10,GPIO_AF_USART3);
    GPIO_PinAFConfig(GPIOB,GPIO_PinSource11,GPIO_AF_USART3);

    // usart6
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOC, &GPIO_InitStructure);
    GPIO_PinAFConfig(GPIOC,GPIO_PinSource6,GPIO_AF_USART6);
    GPIO_PinAFConfig(GPIOC,GPIO_PinSource7,GPIO_AF_USART6);

    // tim4 channel 1 pwm output
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    GPIO_PinAFConfig(GPIOB,GPIO_PinSource6,GPIO_AF_TIM4);

    // adc1 in4/in6
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // touch cs(PB0), touch dout(PB1)
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    // touch sck(PA5)
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // touch din(PB2)
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    // touch pen(PF11)
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_Init(GPIOF, &GPIO_InitStructure);

}
static int extern_mem_init(void)
{
    FSMC_Bank1->BTCR[FSMC_Bank1_NORSRAM3] = 0x5091;
    FSMC_Bank1->BTCR[FSMC_Bank1_NORSRAM3 + 1] = 0x3E100B10;
    FSMC_Bank1E->BWTR[FSMC_Bank1_NORSRAM3] = 0x0FF00B10;

    FSMC_Bank1->BTCR[FSMC_Bank1_NORSRAM4] = 0x5091;
    FSMC_Bank1->BTCR[FSMC_Bank1_NORSRAM4 + 1] = 0x3E100F1F;
    FSMC_Bank1E->BWTR[FSMC_Bank1_NORSRAM4] = 0x0FF00818;

    /* Verify */
    volatile uint16_t *sram3 = (uint16_t *)EXT_SRAM_ADDR;
    for (uint32_t i = 0; i < (EXT_SRAM_SIZE / 2); i++) {
        sram3[i] = (uint16_t)i;
    }
    for (uint32_t i = 0; i < (EXT_SRAM_SIZE / 2); i++) {
        if (sram3[i] != (uint16_t)i) {
            return -1;
        }
    }
    return 0;
}

/* jump to application */
typedef void (*app_func_t)(void);
void driv_jump_to_app(uint32_t address)
{
    uint32_t app_addr = *(uint32_t *)(address + 4);
    app_func_t app = (app_func_t)app_addr;
    __set_MSP(*(uint32_t*)address);
    app();
}

/* reboot */

static volatile uint32_t reboot_ms = 0;
void driv_cpu_reboot(uint32_t ms)
{
    if (ms == 0) {
        NVIC_SystemReset();
        while(1);
    }
    uint32_t primask = __get_PRIMASK();
    __disable_irq();
    reboot_ms = ms;
    __set_PRIMASK(primask);
}

void driv_cpu_reboot_check(void)
{
    if (reboot_ms > 0) {
        reboot_ms --;
        if (reboot_ms == 0) {
            NVIC_SystemReset();
            while(1);
        }
    }
}

int driv_board_init(int is_app)
{
    if(is_app) {
        SCB->VTOR = APP1_BASE;
    } else {
        SCB->VTOR = BOOT_BASE;
    }

    FLASH->ACR |= FLASH_ACR_ICEN;
    FLASH->ACR |= FLASH_ACR_DCEN;
    FLASH->ACR |= FLASH_ACR_PRFTEN;
    RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN;
    RCC->APB1ENR |= RCC_APB1ENR_PWREN;
    PWR->CR &= ~PWR_CR_VOS;
    PWR->CR |= PWR_Regulator_Voltage_Scale1;

    SysTick_Config(SystemCoreClock / 1000);
    peripheral_clock_op(ENABLE);
    gpio_init();
    extern_mem_init();
    return 0;
}

int driv_board_deinit(int is_app)
{
    (void)is_app;
    __disable_irq();
    SysTick->CTRL = 0;
    GPIO_DeInit(GPIOA);
    GPIO_DeInit(GPIOB);
    GPIO_DeInit(GPIOC);
    GPIO_DeInit(GPIOD);
    GPIO_DeInit(GPIOD);
    GPIO_DeInit(GPIOF);
    GPIO_DeInit(GPIOG);
    FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM3, DISABLE);
    FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM4, DISABLE);
    FSMC_NORSRAMDeInit(FSMC_Bank1_NORSRAM3);
    FSMC_NORSRAMDeInit(FSMC_Bank1_NORSRAM4);
    peripheral_clock_op(DISABLE);
    return 0;
}

void driv_enable_irq(uint32_t flag)
{
    __set_PRIMASK(flag);
}
uint32_t driv_disable_irq(void)
{
    uint32_t flag = __get_PRIMASK();
    __disable_irq();
    return flag;
}

/* assert */

#ifdef  USE_FULL_ASSERT
void assert_failed(uint8_t* file, uint32_t line)
{
    (void)file;
    (void)line;
    while (1)
    {
    }
}
#endif /* USE_FULL_ASSERT */


